#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/platform_device.h>
#include <linux/gpio/consumer.h>
#include <linux/of.h>

#define DEV_LED_CNT			    1		  	/* 设备号个数 */
#define DEV_LED_NAME			"drv_led"	/* 名字 */
#define LEDOFF 					0			/* 关灯 */
#define LEDON 					1			/* 开灯 */
#define CLASS_NAME				"dwk_led_class"

#define MIN(a, b) (a < b ? a : b)

static struct ST_LED_DEV{
	dev_t devid;				/*设备号*/
	struct cdev cdev;
	struct class *class;		/*所在类*/
	struct device *device;		/* 设备 */
	int major;					/*主设备号*/
	int minor;					/*次设备号*/
	struct device_node *node;	/*设备节点*/
}st_led_dev;

//static struct ST_LED_DEV st_led_dev = {0};
static struct gpio_desc *led_gpio;


static ssize_t led_drv_read (struct file *file, char *buf, size_t size, loff_t *offset)
{
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
    
	return 0;
}

/* write(fd, &val, 1); */
static ssize_t led_drv_write (struct file *file, const char *buf, size_t size, loff_t *offset)
{
	int err;
	char status;

	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

	err = copy_from_user(&status,buf,1);
	if(err < 0 )
	{
		printk("write fail!\r\n");
	}else{
		printk("write ok! write type = %d,data = %d\r\n",size,status);
	}

    /* 根据次设备号和status控制LED */
	gpiod_set_value(led_gpio, status);

	return 1;
}

static int led_drv_close (struct inode *node, struct file *file)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

static int led_drv_open (struct inode *node, struct file *file)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	
    /*根据次设备号初始化led */
    gpiod_direction_output(led_gpio,0);
    
	return 0;
}
/*1.编写file_operations*/
static struct file_operations led_drv = {
	.owner	 = THIS_MODULE,
	.open    = led_drv_open,
	.read    = led_drv_read,
	.write   = led_drv_write,
	.release = led_drv_close,
};
static struct of_device_id match_led = {
    .compatible = "gpio-leds",
};
static int platform_led_probe(struct platform_device * pdev)
{

    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

    /*获取设备相关信息*/
    led_gpio = gpiod_get(&pdev->dev,"cpu",0);
    if(IS_ERR(led_gpio))
    {
        dev_err(&pdev->dev, "Failed to get GPIO for led\n");
		return PTR_ERR(led_gpio);
    }

    /*创建设备节点*/
    if(st_led_dev.major){
		st_led_dev.devid = MKDEV(st_led_dev.major,0);
		register_chrdev_region(st_led_dev.devid,DEV_LED_CNT,DEV_LED_NAME);
	}else{
		alloc_chrdev_region(&st_led_dev.devid,0,DEV_LED_CNT,DEV_LED_NAME);
		st_led_dev.major = MAJOR(st_led_dev.devid);
		st_led_dev.minor = MINOR(st_led_dev.devid);
	}
	printk("led major = %d,minor = %d\r\n",st_led_dev.major,st_led_dev.minor);

    st_led_dev.cdev.owner = THIS_MODULE;
	cdev_init(&st_led_dev.cdev,&led_drv);

	cdev_add(&st_led_dev.cdev,st_led_dev.devid,DEV_LED_CNT);
    /*创建设备所在类*/
	st_led_dev.class = class_create(THIS_MODULE, CLASS_NAME);
	if(IS_ERR(st_led_dev.class)) {
		gpiod_put(led_gpio);
		cdev_del(&st_led_dev.cdev);
    	unregister_chrdev_region(st_led_dev.devid, DEV_LED_CNT); /* 注销设备号 */
		return PTR_ERR(st_led_dev.class);
		
	}else{
		printk("create class ok,class name = %s\r\n",CLASS_NAME);
	}
    /*创建设备*/
	device_create(st_led_dev.class, NULL, MKDEV(st_led_dev.major, 0), NULL, "dwk_dev_led%d", 0); 

    printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

    return 0;
}

static int platform_led_remove(struct platform_device * pdev)
{

	
    /*先删除设备，再删除类，最后注销设备号*/
    device_destroy(st_led_dev.class, MKDEV(st_led_dev.major, 0));
    cdev_del(&st_led_dev.cdev);
    unregister_chrdev_region(st_led_dev.devid, DEV_LED_CNT); /* 注销设备号 */
    class_destroy(st_led_dev.class);
	
	gpiod_put(led_gpio);

	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

    return 0;
}
static struct platform_driver platform_led = {
    .probe = platform_led_probe,
    .remove = platform_led_remove,
    .driver = {
        .name = "dwk_led_driver",
        .of_match_table = &match_led,
    },
};

/*2.注册模块*/
static int __init drv_led_init(void)
{
    printk("led drv init!\r\n");
    /*注册驱动*/
	platform_driver_register(&platform_led);
    return 0;
}
/*3.销毁*/
static void __exit drv_led_exit(void)
{

    platform_driver_unregister(&platform_led);
    printk("led drv exit!\r\n");
}

module_init(drv_led_init);
module_exit(drv_led_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("dongwenke");
